﻿using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using RainEngine.Controllers;
using RainEngine.SceneNodes.UI;

namespace RainEngine.SceneManagement
{
    /// <summary>
    /// 处理用户与UI的交互
    /// </summary>
    public sealed class UIManager
    {
        #region 成员变量和构造函数

        /// <summary>
        /// 引擎引用
        /// </summary>
        private RainGE  engine;
        
        /// <summary>
        /// 所属场景
        /// </summary>
        private Scene scene;        

        /// <summary>
        /// 所有控件的集合
        /// </summary>
        private List<UISceneNode> uiElements = new List<UISceneNode>();        
        
        /// <summary>
        /// 鼠标左键最后点击的控件索引
        /// </summary>
        public int lastLMousePressed = -1;

        /// <summary>
        /// 鼠标右键最后点击的控件索引
        /// </summary>
        public int lastRMousePressed = -1;

        /// <summary>
        /// 鼠标进入的最后一个控件的索引
        /// </summary>
        public int lastMouseElement = -1;

        /// <summary>
        /// 控件集合的最后一个控件的索引
        /// </summary>
        public int lastTabindex = -1;

        /// <summary>
        /// 存储ZOrder的集合
        /// </summary>
        private IndexList zIndex = new IndexList();

        /// <summary>
        /// 存储tabIndex的集合
        /// </summary>
        private IndexList tabIndex = new IndexList();        
        
        /// <summary>
        /// 当前获取焦点的控件的索引
        /// </summary>
        private int focusIndex =0;
        
        /// <summary>
        /// 集合中可以获取焦点的第一个控件的索引
        /// </summary>
        private int firstAvailableFocusIndex = -1;
       
        /// <summary>
        /// 集合中可以获取焦点的最后一个控件的索引
        /// </summary>
        private int lastAvailableFocusIndex = -1;
       
        /// <summary>
        /// 是否已经设置了集合中可以获取焦点的第一个控件的索引
        /// </summary>
        private bool firstAvailableFocusIndexHasSet = false;        

        /// <summary>
        /// 创建一个新UIManager对象
        /// </summary>
        /// <param name="engine"></param>
        internal UIManager(RainGE engine,Scene setScene)
        {
            this.engine = engine;
            this.scene = setScene;
        }

        #endregion

        #region 属性

        /// <summary>
        /// 返回获取焦点的控件的索引
        /// </summary>
        public int FocusIndex
        {
            get { return focusIndex; }

        }

        /// <summary>
        /// 获取所有控件的集合
        /// </summary>
        public List<UISceneNode> UiElements
        {
            get { return uiElements; }
        }

        /// <summary>
        /// 返回激活的控件，获得焦点的控件为激活控件
        /// </summary>
        public UISceneNode ActiveControl { get { return focusIndex >= 0 ? uiElements[focusIndex] : null; } }        

        #endregion

        #region 更新、绘制方法

        /// <summary>
        /// 处理键盘和鼠标的交互
        /// </summary>
        public void Update(GameTime gameTime)
        {
            if (uiElements.Count == 0)
                return;

            //如果按下Tab或Shift+Tab则将焦点移至下一个/上一个可以接受焦点的控件
            if (Input.KeyboardKeyJustPressed(Keys.Tab))
            {
                int direction;
                if (Input.Keyboard.IsKeyDown(Keys.RightShift) || Input.Keyboard.IsKeyDown(Keys.LeftShift))
                    direction = -1;
                else
                    direction = 1;
                ChangeFocus(direction);
            }

            //-----------------------------------
            //  处理键盘Enter的按下
            //-----------------------------------
            if (Input.KeyboardKeyJustPressed(Keys.Enter))
            {
                uiElements[focusIndex].OnMouseClick(new MouseState(Input.MousePos.X, Input.MousePos.Y, Input.MouseWheelDelta, ButtonState.Pressed, ButtonState.Released, ButtonState.Released, ButtonState.Released, ButtonState.Released));
            }


            //-----------------------------------
            //  处理鼠标事件
            //-----------------------------------

            if (Input.MouseHasMoved())
            {
                //--------------------------------
                //  1. 检测鼠标离开
                //  2. 检测鼠标进入
                //  3. 检测鼠标点击
                //--------------------------------

                //--------------------------------
                //  1. 鼠标离开
                //--------------------------------
                if (lastMouseElement >= 0)
                {
                    if (!uiElements[lastMouseElement].IsPointInside(Input.MousePos.X, Input.MousePos.Y))
                    {
                        uiElements[lastMouseElement].OnMouseLeave();
                        lastMouseElement = -1;
                    }
                }

                //--------------------------------
                //  2. 鼠标进入
                //--------------------------------
                for (int i = 0; i < zIndex.Count; i++)
                {
                    if (uiElements[zIndex[i]].IsPointInside(Input.MousePos.X, Input.MousePos.Y))
                    {
                        //  如果此时的控件不是上一帧的控件才进行下面的操作
                        if (lastMouseElement != zIndex[i])
                        {
                            //  引发鼠标离开事件
                            if (lastMouseElement > -1 && lastMouseElement != zIndex[i])
                            {
                                uiElements[lastMouseElement].OnMouseLeave();
                                lastMouseElement = -1;
                            }
                            uiElements[zIndex[i]].OnMouseEnter();
                            lastMouseElement = zIndex[i];
                            if (uiElements[zIndex[i]].TabStop == true && engine.EnablePlayUISound)
                                //Sound.Play(Sound.Sounds.Highlight);
                                engine.soundEffectHighlight.Play();
                            break;
                        }
                        else
                            break;  //  鼠标已经在最上面ZOrder的控件内，无需再检查下面的控件了
                    }
                }
            }

            //--------------------------------
            //  3. 鼠标点击
            //--------------------------------
            if (lastMouseElement >= 0)  //  如果鼠标在控件内部
            {
                //----------------------------------------
                //  如果鼠标左键处于释放状态
                //----------------------------------------
                if (Input.MouseLeftButtonReleased)
                {
                    if (lastLMousePressed == lastMouseElement && lastLMousePressed >= 0)
                    {
                        //  处理Lost/Got Focus事件(只在控件能够接受焦点的情况下)
                        if (focusIndex != lastLMousePressed && uiElements[lastLMousePressed].TabStop)
                        {
                            uiElements[focusIndex].OnLostFocus();
                            focusIndex = lastLMousePressed;
                            uiElements[focusIndex].OnGotFocus();                            
                        }
                        uiElements[lastLMousePressed].OnMouseClick(new MouseState(Input.MousePos.X, Input.MousePos.Y, Input.MouseWheelDelta, ButtonState.Pressed, ButtonState.Released, ButtonState.Released, ButtonState.Released, ButtonState.Released));
                        lastRMousePressed = -1; //  如果鼠标右键点击则取消左键点击
                    }
                    lastLMousePressed = -1; //  重置鼠标左键点击
                }
                else
                {

                    //----------------------------------------
                    //  如果在控件内部点击 
                    //----------------------------------------
                    if (lastLMousePressed < 0 && lastMouseElement >= 0)
                    {
                        lastLMousePressed = lastMouseElement;
                        //播放鼠标点击的声音
                        if(engine .EnablePlayUISound )
                            engine.soundEffectClick .Play();
                            //Sound.Play(Sound.Sounds.Click );
                    }
                }

                //----------------------------------------
                //  检测鼠标右键点击
                //----------------------------------------
                if (Input.MouseRightButtonReleased )
                {
                    if (lastRMousePressed == lastMouseElement && lastRMousePressed >= 0)
                    {
                        //  处理Lost/Got Focus事件(只在控件能够接受焦点的情况下)
                        if (focusIndex != lastRMousePressed && uiElements[lastRMousePressed].TabStop)
                        {
                            uiElements[focusIndex].OnLostFocus();
                            focusIndex = lastRMousePressed;
                            uiElements[focusIndex].OnGotFocus();                            
                        }
                        uiElements[lastRMousePressed].OnMouseClick(new MouseState(Input.MousePos.X, Input.MousePos.Y, Input.MouseWheelDelta, ButtonState.Released, ButtonState.Released, ButtonState.Pressed, ButtonState.Released, ButtonState.Released));
                        lastLMousePressed = -1; //  如果鼠标左键点击则取消右键点击
                    }
                    lastRMousePressed = -1; //  重置鼠标右键点击
                }
                else
                {
                    //----------------------------------------
                    //  如果在控件内部点击鼠标右键 
                    //----------------------------------------
                    if (lastRMousePressed < 0 && lastMouseElement >= 0)
                        lastRMousePressed = lastMouseElement;
                }
            }
        }

        /// <summary>
        /// 将焦点移至下一个UI控件
        /// </summary>
        /// <param name="direction">移动反向，1表示向下移动，-1表示向上移动</param>
        public void ChangeFocus(int direction)
        {
            for (int i = 1; i <= uiElements.Count; i++)
            {
                int index = focusIndex + (i * direction);
                if (index >= uiElements.Count)
                    index = firstAvailableFocusIndex;
                else if (index < 0)
                    index = lastAvailableFocusIndex;

                if (uiElements[index].TabStop)
                {
                    if (index != focusIndex)
                    {
                        uiElements[focusIndex].OnLostFocus();
                        focusIndex = index;
                        uiElements[index].OnGotFocus();
                        //播放鼠标切换的声音
                        if (engine.EnablePlayUISound)
                            engine.soundEffectHighlight.Play();
                            //Sound.Play(Sound.Sounds.Highlight);
                    }
                    break;
                }
            }
        }

        /// <summary>
        /// 使用SpriteBatch绘制所有控件
        /// </summary>
        public int Draw(GameTime gameTime)
        {
            RainGE.SpriteBatch.Begin(SpriteSortMode.Immediate ,null,null,null,null,null,engine.GlobalTransformation);
            int j=0;
            // 根据ZOrder从后向前绘制控件
            for (int i = zIndex.Count; i > 0; i--)
            {
                if (uiElements[zIndex[i - 1]].Visible)
                {
                    uiElements[zIndex[i - 1]].Draw(gameTime);
                    j += 1;
                }
            }
            RainGE.SpriteBatch.End();
            return j;
        }

        #endregion

        #region 公有方法

        /// <summary>
        /// 在UISceneNodes集合中添加一个控件
        /// </summary>
        /// <param name="element"></param>
        public void AddElement(UISceneNode element)
        {
            zIndex.AddIndex(uiElements.Count);      //  添加当前node的索引用于zorder排序
            tabIndex.AddIndex(uiElements.Count);    //  添加当前node的索引用于tab顺序排序
            uiElements.Add(element);                //  将控件添加到集合中

            if (element.TabIndex == -1 && element.TabStop) //  如果此控件可以接受焦点则添加一个tabindex
            {
                element.TabIndex = (++lastTabindex);
            }
            else if (element.TabIndex > lastTabindex)
            {
                lastTabindex = element.TabIndex;
            }

            //计算场景中可以接受焦点的第一个控件的索引
            if (!firstAvailableFocusIndexHasSet)
            {
                if (!element.TabStop)
                    firstAvailableFocusIndex += 1;
                else
                {
                    firstAvailableFocusIndex += 1;
                    focusIndex = firstAvailableFocusIndex;
                    firstAvailableFocusIndexHasSet = true;
                }
            }
            //计算场景中可以接受焦点的最后一个控件的索引
            if (element.TabStop)
                lastAvailableFocusIndex = uiElements.Count - 1;

            Sort();
        }

        /// <summary>
        /// 从UISceneNodes集合中移除一个UI元素
        /// </summary>
        /// <param name="element"></param>
        public void RemoveElement(UISceneNode element)
        {
            uiElements.Remove(element);
            Sort();
        }

        /// <summary>
        /// 根据ZOrder对所包含的UI元素进行排序
        /// </summary>
        public void Sort()
        {
            zIndex.Sort(new CompareZOrder(uiElements));
            tabIndex.Sort(new CompareTabIndex(uiElements));
        }

        #region 进行比较的辅助类

        //比较Zorder顺序
        private class CompareZOrder : System.Collections.IComparer
        {
            private UISceneNode[] elements;
            public CompareZOrder(List<UISceneNode> elements)
            {
                this.elements = elements.ToArray();
            }
            public int Compare(object x, object y)
            {
                return elements[(int)x].ZOrder - elements[(int)y].ZOrder;
            }
        }

        //比较TabIndex顺序
        private class CompareTabIndex : System.Collections.IComparer
        {
            private UISceneNode[] elements;
            public CompareTabIndex(List<UISceneNode> elements)
            {
                this.elements = elements.ToArray();
            }
            public int Compare(object x, object y)
            {
                return elements[(int)x].TabIndex - elements[(int)y].TabIndex;
            }
        }
        #endregion        

        #endregion

    }
}